<?php
/**

 * tiny_mce_gzip.php

 *

 * Copyright 2010, Moxiecode Systems AB

 * Released under LGPL License.

 *

 * License: http://tinymce.moxiecode.com/license

 * Contributing: http://tinymce.moxiecode.com/contributing

 */
// Handle incoming request if it's a script call
if (TinyMCE_Compressor::getParam("js")) {
 // Default settings
 $tinyMCECompressor = new TinyMCE_Compressor(array(
 /*

	 * Add any site-specific defaults here that you may wish to implement. For example:

	 *

	 * 	"languages" => "en",

	 *  "cache_dir" => realpath(dirname(__FILE__) . "/../../_cache"),

	 *  "files"     => "somescript,anotherscript",

	 *  "expires"   => "1m",

	 */
 ));
 // Handle request, compress and stream to client
 $tinyMCECompressor->handleRequest();
}
/**

 * This class combines and compresses the TinyMCE core, plugins, themes and

 * language packs into one disk cached gzipped request. It improves the loading speed of TinyMCE dramatically but

 * still provides dynamic initialization.

 *

 * Example of direct usage:

 * require_once("../jscripts/tiny_mce/tiny_mce_gzip.php");

 *

 * // Renders script tag with compressed scripts

 * TinyMCE_Compressor::renderTag(array(

 *    "url" => "../jscripts/tiny_mce/tiny_mce_gzip.php",

 *    "plugins" => "pagebreak,style",

 *    "themes" => "advanced",

 *    "languages" => "en"

 * ));

 */
class TinyMCE_Compressor {
 private $files, $settings;
 private static $defaultSettings = array(
  "plugins" => "",
  "themes" => "",
  "languages" => "",
  "disk_cache" => true,
  "expires" => "30d",
  "cache_dir" => "",
  "compress" => true,
  "suffix" => "",
  "files" => "",
  "source" => false,
 );
 /**

	 * Constructs a new compressor instance.

	 *

	 * @param Array $settings Name/value array with non-default settings for the compressor instance.

	 */
 public function __construct($settings = array()) {
  $this->settings = array_merge(self::$defaultSettings, $settings);
  if (empty($this->settings["cache_dir"]))
   $this->settings["cache_dir"] = dirname(__FILE__);
 }
 /**

	 * Adds a file to the concatenation/compression process.

	 *

	 * @param String $path Path to the file to include in the compressed package/output.

	 */
 public function &addFile($file) {
  $this->files .= ($this->files ? "," : "") . $file;
  return $this;
 }
 /**

	 * Handles the incoming HTTP request and sends back a compressed script depending on settings and client support.

	 */
 public function handleRequest() {
  $files = array();
  $supportsGzip = false;
  $expiresOffset = $this->parseTime($this->settings["expires"]);
  $tinymceDir = dirname(__FILE__);
  // Override settings with querystring params
  $plugins = self::getParam("plugins");
  if ($plugins)
   $this->settings["plugins"] = $plugins;
  $plugins = explode(',', $this->settings["plugins"]);
  $themes = self::getParam("themes");
  if ($themes)
   $this->settings["themes"] = $themes;
  $themes = explode(',', $this->settings["themes"]);
  $languages = self::getParam("languages");
  if ($languages)
   $this->settings["languages"] = $languages;
  $languages = explode(',', $this->settings["languages"]);
  $tagFiles = self::getParam("files");
  if ($tagFiles)
   $this->settings["files"] = $tagFiles;
  $diskCache = self::getParam("diskcache");
  if ($diskCache)
   $this->settings["disk_cache"] = ($diskCache === "true");
  $src = self::getParam("src");
  if ($src)
   $this->settings["source"] = ($src === "true");
  // Add core
  $files[] = "tiny_mce";
  foreach ($languages as $language)
   $files[] = "langs/$language";
  // Add plugins
  foreach ($plugins as $plugin) {
   $files[] = "plugins/$plugin/editor_plugin";
   foreach ($languages as $language)
    $files[] = "plugins/$plugin/langs/$language";
  }
  // Add themes
  foreach ($themes as $theme) {
   $files[] = "themes/$theme/editor_template";
   foreach ($languages as $language)
    $files[] = "themes/$theme/langs/$language";
  }
  // Add any specified files.
  $allFiles = array_merge($files, explode(',', $this->settings['files']));
  // Process source files
  for ($i = 0; $i < count($allFiles); $i++) {
   $file = $allFiles[$i];
   if ($this->settings["source"] && file_exists($file . "_src.js")) {
    $file .= "_src.js";
   } else if (file_exists($file . ".js")) {
    $file .= ".js";
   } else {
    $file = "";
   }
   $allFiles[$i] = $file;
  }
  // Generate hash for all files
  $hash = md5(implode('', $allFiles));
  // Check if it supports gzip
  $zlibOn = ini_get('zlib.output_compression') || (ini_set('zlib.output_compression', 0) === false);
  $encodings = (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) ? strtolower($_SERVER['HTTP_ACCEPT_ENCODING']) : "";
  $encoding = preg_match( '/\b(x-gzip|gzip)\b/', $encodings, $match) ? $match[1] : "";
  // Is northon antivirus header
  if (isset($_SERVER['---------------']))
   $encoding = "x-gzip";
  $supportsGzip = $this->settings['compress'] && !empty($encoding) && !$zlibOn && function_exists('gzencode');
  // Set cache file name
  $cacheFile = $this->settings["cache_dir"] . "/" . $hash . ($supportsGzip ? ".gz" : ".js");
   // Set headers
  header("Content-type: text/javascript");
  header("Vary: Accept-Encoding"); // Handle proxies
  header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expiresOffset) . " GMT");
  header("Cache-Control: public, max-age=" . $expiresOffset);
  if ($supportsGzip)
   header("Content-Encoding: " . $encoding);
  // Use cached file
  if ($this->settings['disk_cache'] && file_exists($cacheFile)) {
   readfile($cacheFile);
   return;
  }
  // Set base URL for where tinymce is loaded from
  //$_SERVER["REDIRECT_URL"] ? $uri = $_SERVER["REDIRECT_URL"] : $uri = $_SERVER["PHP_SELF"];//added this in order to properly handle branch urls
  //$buffer = "var tinyMCEPreInit={base:'" . dirname($uri) . "',suffix:''};";		
  $buffer = "var tinyMCEPreInit={base:'" . dirname($_SERVER["SCRIPT_NAME"]) . "',suffix:''};";
  // Load all tinymce script files into buffer
  foreach ($allFiles as $file) {
   if ($file) {
    $fileContents = $this->getFileContents($tinymceDir . "/" . $file);
//				$buffer .= "\n//-FILE-$tinymceDir/$file (". strlen($fileContents) . " bytes)\n";
    $buffer .= $fileContents;
   }
  }
  // Mark all themes, plugins and languages as done
  $buffer .= 'tinymce.each("' . implode(',', $files) . '".split(","),function(f){tinymce.ScriptLoader.markDone(tinyMCE.baseURL+"/"+f+".js");});';
  // Compress data
  if ($supportsGzip)
   $buffer = gzencode($buffer, 9, FORCE_GZIP);
  // Write cached file
  if ($this->settings["disk_cache"])
   @file_put_contents($cacheFile, $buffer);
  // Stream contents to client
  echo $buffer;
 }
 /**

	 * Renders a script tag that loads the TinyMCE script.

	 *

	 * @param Array $settings Name/value array with settings for the script tag.

	 * @param Bool  $return   The script tag is return instead of being output if true

	 * @return String the tag is returned if $return is true  

	 */
 public static function renderTag($tagSettings, $return = false) {
  $settings = array_merge(self::$defaultSettings, $tagSettings);
  if (empty($settings["cache_dir"]))
   $settings["cache_dir"] = dirname(__FILE__);
  $scriptSrc = $settings["url"] . "?js=1";
  // Add plugins
  if (isset($settings["plugins"]))
   $scriptSrc .= "&plugins=" . (is_array($settings["plugins"]) ? implode(',', $settings["plugins"]) : $settings["plugins"]);
  // Add themes
  if (isset($settings["themes"]))
   $scriptSrc .= "&themes=" . (is_array($settings["themes"]) ? implode(',', $settings["themes"]) : $settings["themes"]);
  // Add languages
  if (isset($settings["languages"]))
   $scriptSrc .= "&languages=" . (is_array($settings["languages"]) ? implode(',', $settings["languages"]) : $settings["languages"]);
  // Add disk_cache
  if (isset($settings["disk_cache"]))
   $scriptSrc .= "&diskcache=" . ($settings["disk_cache"] === true ? "true" : "false");
  // Add any explicitly specified files if the default settings have been overriden by the tag ones
  /*

		 * Specifying tag files will override (rather than merge with) any site-specific ones set in the 

		 * TinyMCE_Compressor object creation.  Note that since the parameter parser limits content to alphanumeric

		 * only base filenames can be specified.  The file extension is assumed to be ".js" and the directory is

		 * the TinyMCE root directory.  A typical use of this is to include a script which initiates the TinyMCE object. 

		 */
  if (isset($tagSettings["files"]))
   $scriptSrc .= "&files=" .(is_array($settings["files"]) ? implode(',', $settings["files"]) : $settings["files"]);
  // Add src flag
  if (isset($settings["source"]))
   $scriptSrc .= "&src=" . ($settings["source"] === true ? "true" : "false");
  $scriptTag = '<script type="text/javascript" src="' . htmlspecialchars($scriptSrc) . '"></script>';
  if ($return) {
   return $scriptTag;
  } else {
   echo $scriptTag;
  }
 }
 /**

	 * Returns a sanitized query string parameter.

	 *

	 * @param String $name Name of the query string param to get.

	 * @param String $default Default value if the query string item shouldn't exist.

	 * @return String Sanitized query string parameter value.

	 */
 public static function getParam($name, $default = "") {
  if (!isset($_GET[$name]))
   return $default;
  return preg_replace("/[^0-9a-z\-_,]+/i", "", $_GET[$name]); // Sanatize for security, remove anything but 0-9,a-z,-_,
 }
 /**

	 * Parses the specified time format into seconds. Supports formats like 10h, 10d, 10m.

	 *

	 * @param String $time Time format to convert into seconds.

	 * @return Int Number of seconds for the specified format.

	 */
 private function parseTime($time) {
  $multipel = 1;
  // Hours
  if (strpos($time, "h") > 0)
   $multipel = 3600;
  // Days
  if (strpos($time, "d") > 0)
   $multipel = 86400;
  // Months
  if (strpos($time, "m") > 0)
   $multipel = 2592000;
  // Trim string
  return intval($time) * $multipel;
 }
 /**

	 * Returns the contents of the script file if it exists and removes the UTF-8 BOM header if it exists.

	 *

	 * @param String $file File to load.

	 * @return String File contents or empty string if it doesn't exist.

	 */
 private function getFileContents($file) {
  $content = file_get_contents($file);
  // Remove UTF-8 BOM
  if (substr($content, 0, 3) === pack("CCC", 0xef, 0xbb, 0xbf))
   $content = substr($content, 3);
  return $content;
 }
}
?>
